package com.qys.livesMall.anchor.controller;


import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.crypto.SecureUtil;
import com.alibaba.fastjson2.JSONObject;
import com.qys.livesMall.anchor.model.dto.lives.sys.CreatePullFlowDTO;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
import com.tencentcloudapi.live.v20180801.LiveClient;
import com.tencentcloudapi.live.v20180801.models.*;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.time.ZoneId;
import java.util.Arrays;
import java.util.List;



/**
 * 直播流工具类
 * @author yxh
 */
@Slf4j
@Component
public class LiveStreamUtils {

      /**
       * 应用名
       */
      private static String APP_NAME;
      /**
       * 协议
       */
      public static String AGREEMENT;

      /**
       * 推流域名
       */
      private static String PUSH_DOMAIN;
      /**
       * 推流key
       */
      private static String KEY;

      /**
       * 拉流域名
       */
      private static String PULL_DOMAIN;
      /**
       * 拉流key
       */
      private static String PULL_KEY;

      /**
       * 云API_ID
       */
      private static String YUN_API_KEY;
      /**
       * 云API_SECRET
       */
      private static String YUN_API_SECRET;

      @Value("${txlive.appName}")
      public void setAppName(String appName) {
            LiveStreamUtils.APP_NAME = appName;
      }

      @Value("${txlive.agreement}")
      public void setAGREEMENT(String agreement) {
            LiveStreamUtils.AGREEMENT = agreement;
      }

      @Value("${txlive.pushDomain}")
      public void setPushDomain(String pushDomain) {
            LiveStreamUtils.PUSH_DOMAIN = pushDomain;
      }

      @Value("${txlive.pushKey}")
      public void setKEY(String pushKey) {
            LiveStreamUtils.KEY = pushKey;
      }

      @Value("${txlive.pullDomain}")
      public void setPullDomain(String pullDomain) {
            LiveStreamUtils.PULL_DOMAIN = pullDomain;
      }

      @Value("${txlive.pullKey}")
      public void setPullKey(String pullKey) {
            LiveStreamUtils.PULL_KEY = pullKey;
      }

      @Value("${txlive.yunApiKey}")
      public void setYunApiKey(String yunApiKey) {
            LiveStreamUtils.YUN_API_KEY = yunApiKey;
      }

      @Value("${txlive.yunApiSecret}")
      public void setYunApiSecret(String yunApiSecret) {
            LiveStreamUtils.YUN_API_SECRET = yunApiSecret;
      }

      public static String getPlayUrl(Long userId) {
            String txTime = Long.toHexString(System.currentTimeMillis()/1000 + 24 * 60 * 60).toUpperCase();
            String md5 = SecureUtil.md5(PULL_KEY + userId + txTime);
            return AGREEMENT + "://" + PULL_DOMAIN + "/" + APP_NAME + "/" + userId + "?txSecret=" + md5 + "&txTime=" + txTime;
      }

      public static String getPushUrl(Long userId) {
            return AGREEMENT + "://" + PUSH_DOMAIN + "/" + APP_NAME + "/" + userId + "?" + getSafeUrl(String.valueOf(userId));
      }

      /**
       * 拉流转推
       * @param dto
       * @return 任务ID
       */
      public static String createPullFlow(CreatePullFlowDTO dto) throws TencentCloudSDKException {
            LiveClient client = getClient();
            // 实例化一个请求对象,每个接口都会对应一个request对象
            CreateLivePullStreamTaskRequest req = new CreateLivePullStreamTaskRequest();
            req.setSourceType(dto.getSourceType());
            req.setSourceUrls(dto.getSourceUrls());
            req.setDomainName(PUSH_DOMAIN);
            req.setAppName(APP_NAME);
            req.setStreamName(dto.getStreamName());
            req.setStartTime(LocalDateTimeUtil.format(LocalDateTimeUtil.of(dto.getStartTime(), ZoneId.of("Asia/Shanghai")), "yyyy-MM-dd HH:mm:ss"));
            req.setEndTime(LocalDateTimeUtil.format(LocalDateTimeUtil.of(dto.getEndTime(), ZoneId.of("Asia/Shanghai")),"yyyy-MM-dd HH:mm:ss"));
            req.setOperator("admin");
            // 返回的resp是一个CreateLivePullStreamTaskResponse的实例，与请求对象对应
            log.info("拉流转推,请求参数{}", JSONObject.toJSONString(dto));
            CreateLivePullStreamTaskResponse resp = client.CreateLivePullStreamTask(req);
            log.info("拉流转推,返回结果{}", JSONObject.toJSONString(resp));
            // 输出json格式的字符串回包
            return resp.getTaskId();
      }

      /**
       * 查询某个流的主备流列表
       * @param streamName 主播的userId
       */
      private static DescribeBackupStreamListResponse describeBackupStreamList(String streamName) {
            try{
                  LiveClient client = getClient();
                  // 实例化一个请求对象,每个接口都会对应一个request对象
                  DescribeBackupStreamListRequest req = new DescribeBackupStreamListRequest();
                  req.setStreamName(streamName);
                  // 返回的resp是一个DescribeBackupStreamListResponse的实例，与请求对象对应
                  DescribeBackupStreamListResponse resp = client.DescribeBackupStreamList(req);
                  if(resp.getStreamInfoList().length == 0){
                        try {
                              Thread.sleep(500);
                        } catch (Exception e) {
                              throw new RuntimeException(e);
                        }
                        return describeBackupStreamList(streamName);
                  }
                  // 输出json格式的字符串回包
                  log.info("查询某个流的主备流列表成功:{}", DescribeBackupStreamListResponse.toJsonString(resp));
                  return resp;
            } catch (TencentCloudSDKException e) {
                  log.error("查询某个流的主备流列表失败:{}", e.getMessage());
            }
            return null;
      }

      /**
       * 切换主备流
       * @param streamName 主播userId
       *
       */
      public static void switchBackupStream(String streamName) {

            //推流唯一标识
            String upstreamSequence;

            //查询推推流唯一标识
            DescribeBackupStreamListResponse streamListResponse = describeBackupStreamList(streamName);
            if(streamListResponse == null){
                  return;
            }
            BackupStreamGroupInfo[] streamInfoList = streamListResponse.getStreamInfoList();
            if(streamInfoList.length < 1){
                  return;
            }
            BackupStreamGroupInfo backupStreamGroupInfo = streamInfoList[0];
            BackupStreamDetailData[] backupList = backupStreamGroupInfo.getBackupList();
            if(backupList.length < 2){
                  return;
            }
            List<BackupStreamDetailData> collect = Arrays.stream(backupList).filter(e -> e.getMasterFlag() == 0).toList();
            BackupStreamDetailData backupStreamDetailData = collect.get(0);
            if(backupStreamDetailData == null){
                  return;
            }
            upstreamSequence = backupStreamDetailData.getUpstreamSequence();

            //切换流
            try{
                  LiveClient client = getClient();
                  // 实例化一个请求对象,每个接口都会对应一个request对象
                  SwitchBackupStreamRequest req = new SwitchBackupStreamRequest();
                  req.setStreamName(streamName);
                  req.setAppName(APP_NAME);
                  req.setPushDomainName(PUSH_DOMAIN);
                  req.setUpstreamSequence(upstreamSequence);
                  // 返回的resp是一个SwitchBackupStreamResponse的实例，与请求对象对应
                  SwitchBackupStreamResponse resp = client.SwitchBackupStream(req);
                  // 输出json格式的字符串回包
                  log.info("切换流成功:{}", SwitchBackupStreamResponse.toJsonString(resp));
            } catch (TencentCloudSDKException e) {
                  log.error("切换流失败:{}", e.getMessage());
            }
      }

      /**
       * 删除拉流转推任务
       * @param taskId 任务id
       */
      public static void exampleDeleteAPullTask(String taskId) {
            try{
                  LiveClient client = getClient();
                  // 实例化一个请求对象,每个接口都会对应一个request对象
                  DeleteLivePullStreamTaskRequest req = new DeleteLivePullStreamTaskRequest();
                  req.setTaskId(taskId);
                  req.setOperator("sys");
                  // 返回的resp是一个DeleteLivePullStreamTaskResponse的实例，与请求对象对应
                  DeleteLivePullStreamTaskResponse resp = client.DeleteLivePullStreamTask(req);
                  // 输出json格式的字符串回包
                  log.info("删除拉流任务成功:{}", DeleteLivePullStreamTaskResponse.toJsonString(resp));
            } catch (TencentCloudSDKException e) {
                  log.error("删除拉流任务失败:{}", e.getMessage());
            }
      }


      /**
       * 检查是否在直播中
       * @param streamName 主播userId
       * @return 是/否
       */
      public static boolean checkIfItSLive(String streamName) {
            try{
                  LiveClient client = getClient();
                  // 实例化一个请求对象,每个接口都会对应一个request对象
                  DescribeLiveStreamStateRequest req = new DescribeLiveStreamStateRequest();
                  req.setAppName(APP_NAME);
                  req.setDomainName(PUSH_DOMAIN);
                  req.setStreamName(streamName);
                  // 返回的resp是一个DescribeLiveStreamStateResponse的实例，与请求对象对应
                  DescribeLiveStreamStateResponse resp = client.DescribeLiveStreamState(req);
                  // 输出json格式的字符串回包
                  log.info("检查是否在直播中成功:{}", DescribeLiveStreamStateResponse.toJsonString(resp));
                  return "active".equals(resp.getStreamState());
            } catch (TencentCloudSDKException e) {
                  log.info("检查是否在直播中失败:{}", e.getMessage());
                  return true;
            }
      }


      @NotNull
      private static LiveClient getClient() {
            // 实例化一个认证对象，入参需要传入腾讯云账户 SecretId 和 SecretKey，此处还需注意密钥对的保密
            // 代码泄露可能会导致 SecretId 和 SecretKey 泄露，并威胁账号下所有资源的安全性。以下代码示例仅供参考，建议采用更安全的方式来使用密钥，请参见：https://cloud.tencent.com/document/product/1278/85305
            // 密钥可前往官网控制台 https://console.cloud.tencent.com/cam/capi 进行获取
            Credential cred = new Credential(YUN_API_KEY, YUN_API_SECRET);
            // 实例化一个http选项，可选的，没有特殊需求可以跳过
            HttpProfile httpProfile = new HttpProfile();
            httpProfile.setEndpoint("live.tencentcloudapi.com");
            // 实例化一个client选项，可选的，没有特殊需求可以跳过
            ClientProfile clientProfile = new ClientProfile();
            clientProfile.setHttpProfile(httpProfile);
            // 实例化要请求产品的client对象,clientProfile是可选的
            return new LiveClient(cred, "ap-beijing", clientProfile);
      }


      private static final char[] DIGITS_LOWER =
            {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};


      /**
       * KEY+ streamName + txTime 拼接
       * @param streamName
       * @return
       */
      private static String getSafeUrl(String streamName) {

            String txTime = Long.toHexString(System.currentTimeMillis()/1000 + 24 * 60 * 60).toUpperCase();

            String input = new StringBuilder().
                              append(KEY).
                              append(streamName).
                              append(txTime).toString();


            String txSecret = null;
            try {
                  MessageDigest messageDigest = MessageDigest.getInstance("MD5");
                  txSecret  = byteArrayToHexString(
                              messageDigest.digest(input.getBytes("UTF-8")));
            } catch (NoSuchAlgorithmException e) {
                  e.printStackTrace();
            } catch (UnsupportedEncodingException e) {
                  e.printStackTrace();
            }


            return txSecret == null ? "" :
                              new StringBuilder().
                              append("txSecret=").
                              append(txSecret).
                              append("&").
                              append("txTime=").
                              append(txTime).
                              toString();
      }


      private static String byteArrayToHexString(byte[] data) {
            char[] out = new char[data.length << 1];


            for (int i = 0, j = 0; i < data.length; i++) {
                  out[j++] = DIGITS_LOWER[(0xF0 & data[i]) >>> 4];
                  out[j++] = DIGITS_LOWER[0x0F & data[i]];
            }
            return new String(out);
      }

}


